﻿using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;

namespace Devonline.AspNetCore;

/// <summary>
/// 附加信息类服务
/// 每个 TEntitySet 类型中的 TAdditional 类型的附加信息认为有且仅有一个
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
/// <typeparam name="TEntitySet">业务数据类型</typeparam>
/// <typeparam name="TAdditional">附加信息数据类型</typeparam>
/// <typeparam name="TKey">主键类型</typeparam>
public class DataWithAdditionalService<TDbContext, TEntitySet, TAdditional, TKey> :
    DataService<TDbContext, TEntitySet, TKey>,
    IDataWithAdditionalService<TDbContext, TEntitySet, TAdditional, TKey>,
    IDataService<TDbContext, TEntitySet, TKey>,
    IDataAdditionalService<TDbContext, TEntitySet, TAdditional, TKey>
    where TDbContext : DbContext
    where TEntitySet : class, IEntitySet<TKey>
    where TAdditional : class, IAdditional<TKey>
    where TKey : IConvertible
{
    protected readonly DbSet<TAdditional> _dbSetAdditional;
    public DataWithAdditionalService(
        TDbContext context,
        ILogger<DataWithAdditionalService<TDbContext, TEntitySet, TAdditional, TKey>> logger,
        IDistributedCache cache,
        IHttpContextAccessor httpContextAccessor,
        AppSetting appSetting
        ) : base(context, logger, cache, httpContextAccessor, appSetting)
    {
        _dbSetAdditional = _context.Set<TAdditional>();
    }

    #region 实现自 IDataAdditionalService 接口的方法
    /// <summary>
    /// 新增业务数据的附加信息
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="additionals">附加数据集合</param>
    /// <returns></returns>
    public virtual async Task AddAdditionalsAsync(TEntitySet entitySet, IEnumerable<TAdditional>? additionals = default)
    {
        additionals ??= entitySet.GetCollectionMember<TEntitySet, TAdditional>();
        if (additionals is not null && additionals.Any())
        {
            foreach (var additional in additionals)
            {
                additional.AdditionalId = entitySet.Id;
                Create(additional);
                await _dbSetAdditional.AddAsync(additional);
            }
        }
    }
    /// <summary>
    /// 更新业务数据的附加信息
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task UpdateAdditionalsAsync(TEntitySet entitySet, IEnumerable<TAdditional>? additionals = default, bool isLogical = false)
    {
        var news = additionals ?? entitySet.GetCollectionMember<TEntitySet, TAdditional>() ?? new List<TAdditional>();
        var olds = await _dbSetAdditional.Where(x => x.AdditionalId != null && x.AdditionalId.Equals(entitySet.Id)).ToListAsync() ?? new List<TAdditional>();
        if (!news.Any() && !olds.Any())
        {
            return;
        }

        foreach (var entity in news)
        {
            var old = olds.FirstOrDefault(x => x.Id != null && x.Id.Equals(entity.Id));
            if (old == null)
            {
                //新的有旧的没有, 则新增
                entity.AdditionalId = entitySet.Id;
                Create(entity);
                _dbSetAdditional.Add(entity);
            }
            else
            {
                //新的有旧的也有, 则更新
                //先对原数据进行逻辑更新
                Update(old, isLogical);
                old.Index = entity.Index;
                old.Key = entity.Key;
                old.Value = entity.Value;
                Update(old);
                _dbSetAdditional.Update(old);
            }
        }

        foreach (var old in olds)
        {
            if (!news.Any(x => x.Id != null && x.Id.Equals(old.Id)))
            {
                //旧的有新的没有, 则删除
                //对原数据进行逻辑更新
                Delete(old, isLogical);
            }
        }
    }
    /// <summary>
    /// 删除业务数据的附加信息
    /// </summary>
    /// <param name="id">业务数据主键</param>
    /// <param name="additionals">附加数据集合</param>
    /// <param name="isLogical">是否逻辑操作, 默认不是</param>
    /// <returns></returns>
    public virtual async Task DeleteAdditionalsAsync(TKey id, bool isLogical = false)
    {
        var additionals = await _dbSetAdditional.Where(x => x.AdditionalId != null && x.AdditionalId.Equals(id)).ToListAsync();
        if (additionals is not null && additionals.Any())
        {
            foreach (var additional in additionals)
            {
                Delete(additional, isLogical);
            }
        }
    }
    #endregion

    #region 重载自 DataService 内部方法
    /// <summary>
    /// 此重载新增方法会同时将当前业务数据的附加信息一并新增
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    protected override async Task InternalAddAsync(TEntitySet entitySet, DataServiceContext? context = null)
    {
        _logger.LogInformation("The user {user} will add {typeName} and it's additionals, the content is: " + entitySet.ToJsonString(), UserName, _typeName);

        await base.InternalAddAsync(entitySet, context);
        await AddAdditionalsAsync(entitySet, entitySet.GetCollectionMember<TEntitySet, TAdditional>());
    }
    /// <summary>
    /// 重载的更新方法会同时将当前类型的附加信息一并更新
    /// </summary>
    /// <param name="entitySet">业务数据</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    protected override async Task InternalUpdateAsync(TEntitySet entitySet, DataServiceContext? context = null)
    {
        var isLogical = context?.IsLogical ?? false;
        _logger.LogInformation("The user {user} will {isLogical} update {typeName} and it's additionals, the content is: " + entitySet.ToJsonString(), UserName, GetLogicalString(isLogical), _typeName);

        await base.InternalUpdateAsync(entitySet, context);
        await UpdateAdditionalsAsync(entitySet, entitySet.GetCollectionMember<TEntitySet, TAdditional>(), isLogical);
    }
    /// <summary>
    /// 重载的删除方法会同时将当前业务数据的附加信息一并删除
    /// </summary>
    /// <param name="id">业务数据主键</param>
    /// <param name="context">数据操作上下文</param>
    /// <returns></returns>
    protected override async Task InternalDeleteAsync(TKey id, DataServiceContext? context = null)
    {
        var isLogical = context?.IsLogical ?? false;
        _logger.LogInformation("The user {user} will {isLogical} delete {typeName} and it's additionals, the content id is: " + id, UserName, GetLogicalString(isLogical), _typeName);

        await base.InternalDeleteAsync(id, context);
        await DeleteAdditionalsAsync(id, isLogical);
    }
    #endregion
}

/// <summary>
/// 字符串作为主键的附加信息类服务
/// 每个 TEntitySet 类型中的 TAdditional 类型的附加信息认为有且仅有一个
/// </summary>
/// <typeparam name="TDbContext">数据库上下文</typeparam>
/// <typeparam name="TEntitySet">业务数据类型</typeparam>
/// <typeparam name="TAdditional">附加信息数据类型</typeparam>
public class DataWithAdditionalService<TDbContext, TEntitySet, TAdditional> :
    DataWithAdditionalService<TDbContext, TEntitySet, TAdditional, string>,
    IDataWithAdditionalService<TDbContext, TEntitySet, TAdditional>,
    IDataService<TDbContext, TEntitySet>
    where TDbContext : DbContext
    where TEntitySet : class, IEntitySet<string>
    where TAdditional : class, IAdditional<string>
{
    public DataWithAdditionalService(
        TDbContext context,
        ILogger<DataWithAdditionalService<TDbContext, TEntitySet, TAdditional>> logger,
        IDistributedCache cache,
        IHttpContextAccessor httpContextAccessor,
        AppSetting appSetting
        ) : base(context, logger, cache, httpContextAccessor, appSetting) { }
}